【严格模式】StrictMode 引发的组件重复执行 您所在的位置:网站首页 js click事件多次执行 【严格模式】StrictMode 引发的组件重复执行

【严格模式】StrictMode 引发的组件重复执行

2023-03-23 14:01| 来源: 网络整理| 查看: 265

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。

前言

2022年3月29号,React 18正式版发布,小明也兴冲冲地开始hooks学习之旅。某天他使用codesandbox写了一个小demo时,他发现组件诡异地渲染了多次,函数组件代码如下:

count.jsx

export default function Count() {  const [counter, setCounter] = useState(0);  useEffect(() => {    console.log("effect");    setTimeout(() => {      setCounter(counter + 1);   }, 3000); });  console.log("before render"); ​  return (          {counter}     ); } 复制代码

实现的效果很简单,每隔三秒将count加1,很简单对不对?

打开这个 demo,同时打开控制台,你就可以看到如下输入:

CPT2204012224-580x267.gif

before render与effect一开始都打印了两次,之后before render每次都诡异地打印了两次

React.StrictMode

这是为什么呢?经过一番查找,发现是React.StrictMode的锅。React 17文档中是这样描述 React.StrictMode:

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。

React 17文档中关于它的作用大概可以归为两类:

检测副作用 对于在应用中使用已经废弃、过时的方法会发出警告

对于第二点,相信大家都可以理解,毕竟React现在都发布18版了,不少以前的方法已经过时或者废弃了,在较新版本的React中再使用这些方法肯定时不安全的。

那么对于第一点呢?

这不得不提React 18文档对于StrictMode的描述:

React offers a “Strict Mode” in which it calls each component’s function twice during development.

大意就是在开发者模式中,StrictMode会将相应组件执行两次,这下重复执行的疑惑解决了。

但新的疑问产生了,文档中一直提的副作用又是啥?副作用这个词在React 17文档中提到的次数很多,如何理解它呢?

在计算机科学中,函数副作用指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响 -- 维基百科

举个通俗易懂的例子:张三不小心感冒了,鼻塞流涕,医生给他开了感冒药,感冒药的作用就是让我们的身体恢复健康,但是服用的过程中,张三感冒的症状的确是减轻了,但同时他感到浑身乏力、嗜睡,这就副作用,即意料之外的结果。

事实上,React 18的文档提到的更多的是purity,即纯度,这其实是函数式编程的理念,这与React 17文档中提到的无副作用是一个意思,react hooks函数式组件实际上就是函数式编程理念的体现。编写纯函数带来了一定的心智负担,但随着开发者对其接受度的提高,新文档中大量使用了purity进行相关描述。文档中提到,纯函数带来了以下优势:

多环境运行。例如可以运行在服务端,因为同样的输入,总是对应同样的输出,因此组件可以被其他人复用; 减少重复渲染。如果函数组件的输入没有改变,直接复用就好啦,不需要重复渲染。 随时中断渲染。在渲染层级较深的组件树时,数据发生了改变,那么React可以马上重新开始渲染,而不用等待过时的渲染完成。

因此StrictMode就是在开发中帮助我们进行检测,保证我们编写的函数组件都是 '纯' 的,这也就解释了为什么开头提到的为什么组件会执行两次,StrictMode会多执行一次,两次执行的结果相同,证明我们编写的的确是纯函数。

实例

以下列举了一些引发StrictMode的其他例子

在函数内部修改一个已经存在的变量 let guest = 0; function Cup() {  // Bad: changing a preexisting variable!  guest = guest + 1;  return Tea cup for guest #{guest}; } export default function TeaSet() {  return (                           ) } 复制代码

很明显,当这个组件执行多次,guest的值是逐渐增长的,回想一下纯函数的定义,上述函数组件不是纯函数,因此StrictMode会进行警告。

useState

下面的组件定义了counter变量,我们给useState传入了初始化函数,组件首次运行时会执行一次这个函数,返回的值作为counter的初始值,在之后的执行中这个初始化函数会被忽略。

此外,在使用setState改变counter的值时,我们为其提供了一个updater函数,当点击按钮时会更新counter的值。

因此,理论上首先会打印一次initializer,然后每按一次按钮,都会打印一次updater

import React, { useState } from "react"; ​ export default function Count2() {  const [counter, setCounter] = useState(() => {    console.log('initializer')    return 0 });    const handleClick = function() {    setCounter(() => {      console.log('updater')      return counter+1   }) } ​  return (          {counter}      增加     ); } 复制代码

demo

而事实上,StrictMode会帮我们把这两个函数都调用两次,保证其为纯函数。

总结

React.StrictMode在开发模式下会重复调用组件,保证我们编写的组件

检测意外的副作用,确保函数组件时纯函数 对于在应用中使用已经废弃、过时的方法会发出警告 仅在开发者模式下运行,不影响生产构建 参考

Side Effects: (un)intended consequences

# 略微探究React StrictMode两次渲染的问题

严格模式



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有